Add Blend v2 integration test suite#664
Open
aditya1702 wants to merge 25 commits into
Open
Conversation
aditya1702
force-pushed
the
blend/pr5-graphql
branch
from
July 15, 2026 20:52
ca9546f to
0caadc2
Compare
aditya1702
force-pushed
the
blend/pr6-integration-tests
branch
from
July 15, 2026 20:52
6f67e5f to
39422d7
Compare
aditya1702
force-pushed
the
blend/pr5-graphql
branch
from
July 16, 2026 16:56
0caadc2 to
b67919d
Compare
aditya1702
force-pushed
the
blend/pr6-integration-tests
branch
from
July 16, 2026 16:56
39422d7 to
a98eef2
Compare
aditya1702
force-pushed
the
blend/pr5-graphql
branch
from
July 16, 2026 20:07
b67919d to
ddde2c9
Compare
aditya1702
force-pushed
the
blend/pr6-integration-tests
branch
2 times, most recently
from
July 21, 2026 18:45
8cc6a07 to
7cf40f1
Compare
aditya1702
force-pushed
the
blend/pr5-graphql
branch
from
July 21, 2026 18:45
ddde2c9 to
66a77c9
Compare
aditya1702
force-pushed
the
blend/pr6-integration-tests
branch
from
July 22, 2026 19:26
d67184b to
e29e695
Compare
aditya1702
force-pushed
the
blend/pr5-graphql
branch
from
July 22, 2026 19:26
66a77c9 to
317d483
Compare
aditya1702
force-pushed
the
blend/pr6-integration-tests
branch
from
July 22, 2026 19:36
e29e695 to
930f6ad
Compare
aditya1702
force-pushed
the
blend/pr5-graphql
branch
from
July 22, 2026 19:36
317d483 to
227b47b
Compare
aditya1702
force-pushed
the
blend/pr6-integration-tests
branch
from
July 22, 2026 20:02
930f6ad to
64ce80f
Compare
aditya1702
force-pushed
the
blend/pr5-graphql
branch
from
July 22, 2026 20:02
227b47b to
9d416df
Compare
…laim math Claimable BLND was computed against the stored emission index, which is only as fresh as the stream's last on-chain touch — idle streams under-reported until someone else interacted. ProjectEmissionIndex replicates the contract distributors' update_emission_data step (blend-contracts-v2 @ ba22b487, pool and backstop distributor.rs; mirrored by blend-sdk-js Emissions.accrue): index + floor(min(now,expiration)- last_time)*eps*scalar/supply with the contract's exact guards. Pool streams project over the side's raw b/dToken supply at 10^decimals; the backstop projects over unqueued shares (shares - q4w) at 1e7. Contract-test vectors (test_update_emission_data_past_exp/_rounds_down) pinned bit-exact in rates_test.go.
emissionsAPRFor call sites passed the stored BRate/DRate while every sibling token/USD figure on the same types is valued at the projected PB/PD, so emissions APRs were overstated after idle accrual. All surviving call sites (position reserve, pool catalog) now pass PB/PD.
…d-sdk-js BlendPoolPosition.netApy divided the weighted APY spread by the net position value (supplied - borrowed), which diverges from blend-sdk-js's PositionsEstimate — the convention the Blend UI displays — and explodes as a leveraged position's net value approaches 0. Now (Σ suppliedUsd·supplyApy − Σ borrowedUsd·borrowApy) / Σ suppliedUsd, with the SDK's supply-less rule: exactly 0 (debt is forgiven as bad debt).
…eams Blend runs two independent emission streams per reserve; the single emissionsApr picked the bToken side whenever supply/collateral existed, silently dropping the borrow-stream APR for both-sides positions. BlendReservePosition now mirrors BlendReserve with emissionsSupplyApr + emissionsBorrowApr, each the stream's pool-wide APR at projected rates.
Earn discovery is a downstream composition concern: blendPools already carries every field the asset-first view derived (status gating, reserve enabled, supplyApy, emissionsSupplyApr, suppliedUsd ordering, token metadata), so consumers assemble it themselves. Drops the query, its BlendEarnOption/BlendEarnPoolOption types, resolver, complexity entry, and the now-unused ReserveModel.GetAll reader.
…te accrual Adversarial audit vs blend-contracts-v2 @ ba22b487 found two edge-state deviations in ProjectRates: - util==0: the contract bumps last_time and skips the rate update when nobody borrows; the projection grew dRate by irMod*rBase. Masked today (every pD consumer multiplies a zero dToken amount) but a landmine. - bad debt (liabilities >= supply): the contract grows bRate by accrued*(1-bstop)/totalSupply — the UNCLAMPED liabilities/supply ratio — while the reduction reused the clamped utilization, so suppliers' bRate growth was understated (~0.5% over 30 idle days). ProjectRates now takes bSupply/dSupply, derives the raw ratio itself, and mirrors the contract's on-load guards (zero bSupply / zero liabilities). New vectors: util==0, zero bSupply, bad-debt raw ratio.
…ry/Account BlendPool.status becomes a BlendPoolStatus enum (ADMIN_ACTIVE..SETUP, matching the BlendAuctionType precedent); null until ingested and for any unrecognized on-chain value. The blend entry points move off 'extend type' blocks into the base Query (queries.graphqls) and Account (account.graphqls) definitions so each type reads complete in one place; blend.graphqls keeps all Blend object/enum types.
Nested blend lists were priced once under a single outer multiplier, so a full blendPools selection cost ~29 units per pool instead of pools x reserves. Every list level now carries its own bound: catalog x50, per-account pools/backstop/activeAuctions x10 (documented assumption), reserves and auction bid/lot x30 (MAX_RESERVES), q4w x20 (MAX_Q4W_SIZE); Account.blendPositions drops back to default pricing so cardinality is not double-charged. Full-selection worst cases are now blendPools=26150 and blendPositions=7572 - above the current 6000 deployment limit by design; admitting full selections needs a deployment-side limit raise. Existing non-blend entries (freighter budget) untouched.
…ct guard The distributor advances the stream index before any user write and traps on a negative delta via require_nonnegative; off-chain a negative delta can only mean an ingestion gap between the stream row and the user row. ClaimableEmissions now returns the accrued balance alone in that state instead of a negative claimable.
GetBackstopLPPrices inferred the protocol-wide BLND/LP price group from a self-priced-row join, but a self-priced row does not uniquely identify the configured Comet: pools are permissionless, so any pool whose oracle is also one of its reserve assets produces the same shape, and a stale formerly-configured Comet's rows linger until they age out. BLEND_BACKSTOP_LP_CONTRACT_ID - the same pin the snapshot writer targets - now flows into serve (cmd/serve.go -> Configs -> resolver) and scopes the query to exactly that oracle; findBackstopPrices reduces to picking the self-priced LP row and its BLND sibling within the pinned group. An unset pin returns no rows, so backstop USD fields resolve to null. Deploy note: the api deployment needs BLEND_BACKSTOP_LP_CONTRACT_ID in its env (ingest already has it).
…ping, backstop-pool relationship - interestEarned/interestPaid: lifetime cost-basis semantics on the schema (token-denominated, survives full exit, liquidation fills adjust the cost basis, may go slightly negative). - LendingChange: per-reason population table for tokenId/amount/poolId, verified against the event decoder (events.go) - which reasons carry the reserve asset vs the BLND SAC vs null, share- vs LP- vs underlying-denominated amounts, and the backstop CLAIM's null poolId. - BlendBackstopPosition: the backstop is one protocol-wide contract holding per-pool first-loss capital - one entry per backed pool. - buildBackstopPosition godoc: un-ingested backstop pool row displays lpTokens 0 (under-reports, never over-reports). - Utilization godoc: the contract descales with ceil/floor then ceils the 7-decimal ratio; the exact rational here is <= that, by <1e-7 for funded reserves.
…or tables Read blend_pool_claimed/blend_backstop_claimed via the new GetByAccount readers after phase-2 live ingestion and assert the supplier's pool claim and whale's backstop claim folded positive totals. Guard that phase-1 (no claims) leaves both accumulator tables empty.
…s survive The canonical-backstop pin resolves to empty on the standalone network, which made the processor drop every backstop-shaped entry and event as a non-canonical impostor — BlendMigrationTestSuite failed on the whale's missing blend_backstop_positions row. The suite deploys the backstop from the master account (keypair.Root of the passphrase) with a fixed salt, so its address is a deterministic function of the passphrase alone. Pin it in canonicalBackstopAddress the same way blndTokenAddress pins the standalone BLND SAC: unit test + a deploy-time assertion in SetupBlendStack.
Absorbs the review-round API changes on blend/pr5-graphql: drops the GetBlendEarnOptions client method, query, and types (the query was removed - earn discovery composes from blendPools); BlendPool.status becomes the BlendPoolStatus enum string; BlendReservePosition's single emissionsApr splits into emissionsSupplyApr/emissionsBorrowApr. Claimable-emission assertions were already sign-based, so the projected-to-now claimable math needs no test changes.
aditya1702
force-pushed
the
blend/pr6-integration-tests
branch
from
July 22, 2026 22:45
64ce80f to
81c3229
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Deploys the full Blend v2 protocol stack (Comet BLND:USDC LP, mock SEP-40 oracle, emitter, pool factory, backstop, one pool with USDC/XLM reserves) on the standalone integration-test network — replicating blend-utils'
mock-example.tsdeployment order (pinned commitb05242df, wasm provenance + sha256s ininfrastructure/testdata/blend/README.md) — then executes real Blend operations and asserts DB state and GraphQL responses through both ingestion paths.Coverage
new_auction→ 50% fill; both counterparty rows + auction position folds), BACKSTOP_DEPOSIT, BACKSTOP_WITHDRAW_QUEUE, BACKSTOP_WITHDRAW_CANCEL.claimedBlnd,backstopClaimedLp,token_id = -1backstop streams), exactnet_supplied/net_borrowedcost-basis folds, Q4W JSONB with the ~17-day expiration observed live over GraphQL while queued, and lifetime claimed totals (blend_pool_claimed/blend_backstop_claimedfold positive after the phase-2 claims, with a phase-1 both-empty guard).BlendMigrationTestSuiterunsprotocol-setup→protocol-migrate current-state→protocol-migrate history(datastore backend, MinIO-fed by the ledger exporter) with cursor-handoff pre/post assertions — the history mode's first automated coverage.BlendLiveIngestionTestSuiterestarts ingest withBLEND_PRICE_INTERVAL/BLEND_BACKSTOP_LP_CONTRACT_IDand asserts the full Freighter-facing GraphQL surface (blendPools,blendPool,account.blendPositions, LENDING state changes).Not covered end-to-end (unit tests only)
ARTIFICIALLY_ACCELERATE_TIME_FOR_TESTINGonly accelerates ledger cadence, not close time, so the lockup cannot elapse on standalone.Production changes
blndTokenAddress(internal/services/blend/validator.go) gains a standalone-network case: the suite issues BLND from the network master account (keypair.Rootof the passphrase), so its SAC address is a fixed constant, letting pool-CLAIM rows carry the real BLNDtoken_idon standalone. Pinned by a unit test and asserted at deploy time in the test setup.canonicalBackstopAddress(same file) gains the matching standalone-network case: the processor folds backstop-shaped entries/events only from the canonical backstop, and on an unpinned network that pin resolves to empty and drops all backstop state (which is what failed this suite — the whale'sblend_backstop_positionsrow never materialized). The suite deploys the backstop from the master account with a fixed salt, so its address is deterministic and pinned the same way: unit test + deploy-time assertion.pkg/wbclientgains Blend query methods (GetBlendPools,GetBlendPool,GetAccountBlendPositions) andLendingChangestate-change decoding, with unit tests.Test infrastructure added
executeSorobanOperationAs): arbitrary tx source, RPC-fresh sequences, simulation-provided auth nonces (the existing helper hardcodes nonce 0, safe only for the master account's first use).Blend phase adds ~100s to
make integration-test(total run ~4.5 min, well under the 30m timeout). Verified green twice, including aFORCE_REBUILD=truefrom-scratch run.🤖 Generated with Claude Code